package com.killer.context;

import com.killer.anno.*;
import com.killer.factory.BeanFactory;
import com.killer.factory.DefaultListableBeanFactory;
import com.killer.handler.InterceptorDefinition;
import com.killer.pojo.BeanDefinition;
import com.killer.pojo.PropertyValue;
import com.killer.util.PackageSacanUtil;
import com.killer.util.StringUtil;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author ：Killer
 * @date ：Created in 20-10-26 上午9:44
 * @description：${description}
 * @modified By：
 * @version: version
 */
public class XmlWebApplicationContext extends AbstractApplicatonContext {


    /**
     * 保存已注册的过滤器
     */
    private List<InterceptorDefinition> interceptors=new ArrayList<InterceptorDefinition>();


    public XmlWebApplicationContext(String location) throws Exception{
        //将xml文件以流的方式加载到内存
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location);
        //创建bean工厂
        refreshBeanFactory();
        //加载beanDefition
        registerBeanDefinition(inputStream);
        //实例化bean
        initializeBean();
    }

    private void initializeBean() throws Exception {
        for (Map.Entry<String, BeanDefinition> entry : beanFactory.getRegisterFactory().entrySet()) {
            getBean(entry.getKey());
        }
    }

    private void refreshBeanFactory() {
        getBeanFactory();
    }

    /**
     * 解析xml文件,将定义的bean以及相关的报扫描加载成bean对象
     * @param inputStream
     */
    public void registerBeanDefinition(InputStream inputStream) throws Exception {
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element rootElement = document.getRootElement();
        //解析bean标签
        parseBeanLabel(rootElement);
        //解析包扫描标签
        parseComponentScan(rootElement);
        //解析拦截器标签
        parseInterceptors(rootElement);
    }

    private void parseInterceptors(Element rootElement) throws Exception {
        //查找所有的mvc:interceptor标签
        List<Element> interceptorElements = rootElement.selectNodes("//interceptor");
        for (Element interceptorElement : interceptorElements) {
            //查找下面的bean标签
            List<Element> beanElements=interceptorElement.selectNodes("bean");
            //解析注册beanDefiniton对象  这里只能有一个bean标签，因此只会循环一次
            for (Element beanElement : beanElements) {
                BeanDefinition beanDefinition=parseBeanDefinition(beanElement);
                //获取路径
                List<String> paths = parseInterceptorPath(interceptorElement);
                //该循环只会进入一次，因此不用担心重复创建
                InterceptorDefinition handlerInterceptor = new InterceptorDefinition(beanDefinition.getId(), paths);
                interceptors.add(handlerInterceptor);
            }
        }
    }

    private List<String> parseInterceptorPath(Element interceptorElement) {
        List<String> paths=new ArrayList<String>();
        //这里也是为了简化，就不实现exclude了
        List<Element> pathElements = interceptorElement.selectNodes("mapping");
        for (Element pathElement : pathElements) {
            paths.add(pathElement.attributeValue("path"));
        }
        return paths;
    }

    private void parseComponentScan(Element rootElement) throws Exception {
        //查找所有的context:component-scan标签
        List<Element> scanElements = rootElement.selectNodes("//component-scan");
        for (Element scanElement : scanElements) {
            //获取包名称
            String packageName=scanElement.attributeValue("base-package");
            //获取该包以及子包下所有的class
            List<String> classNames = PackageSacanUtil.scanPackage(packageName);
            //将有对应注解的class注册成beanDefinition对象
            parseBeanDefinition(classNames);
        }

    }

    private void parseBeanDefinition(List<String> classNames) throws Exception {
        for (String className : classNames) {
            Class<?> clazz = Class.forName(className);
            //判断是否有关键注解
            if (checkAnno(clazz)) {
                //这里为了简化，就不允许自定义beanName了，统一用类名首字母小写做beanId
                String beanId=className.substring(0,className.lastIndexOf("."))+"."+ StringUtil.lowerFisrtChar(className.substring(className.lastIndexOf(".")+1));
                //解析字段属性
                List<PropertyValue> propertyValues = parsePropertyValues(clazz);
                //这里先简单化，就不去实现是否需要产生代理对象了
                BeanDefinition beanDefinition = new BeanDefinition(beanId, clazz, propertyValues);
                beanFactory.registerBean(beanDefinition);
            }
        }
    }

    private List<PropertyValue> parsePropertyValues(Class clazz) throws Exception {
        List<PropertyValue> propertyValues = new ArrayList<PropertyValue>();
        //获取该类的所有字段
        Field[] declaredFields = clazz.getDeclaredFields();
        if (declaredFields != null) {
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                //先判断是否有Value注解,有则直接赋值就好
                Value value = declaredField.getAnnotation(Value.class);
                //这里就简化不做判断是不是数值类型，因为重点不在这，全部先当字符串好了
                if (value != null) {
                    propertyValues.add(new PropertyValue(declaredField.getName(), value.value(), null));
                    //有value注解就不会再有autowired注解了
                    continue;
                }
                Autowired autowired = declaredField.getAnnotation(Autowired.class);
                if (autowired != null) {
                    //该注解代表注入某个在容器中的对象
                    //先后去该字段类型
                    Class<?> fileType = declaredField.getType();
                    propertyValues.add(new PropertyValue(declaredField.getName(), null, StringUtil.lowerFisrtChar(fileType.getName())));
                }
            }
        }
        return propertyValues;
    }

    private boolean checkAnno(Class<?> clazz) {
        return clazz.isAnnotationPresent(Component.class) || clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class);
    }

    private void parseBeanLabel(Element rootElement) throws ClassNotFoundException {
        //查找所有的bean标签
        List<Element> beanElements = rootElement.selectNodes("//bean");
        for (Element beanElement : beanElements) {
            parseBeanDefinition(beanElement);
        }

    }

    private BeanDefinition parseBeanDefinition(Element beanElement) throws ClassNotFoundException {
        //获取id
        String id = beanElement.attributeValue("id");
        //获取类型名称
        String className = beanElement.attributeValue("class");
        if (id == null) {
            //如果没有设置id，全限定类名的类名小写作id
            id=className.substring(0,className.lastIndexOf("."))+"."+ StringUtil.lowerFisrtChar(className.substring(className.lastIndexOf(".")+1));
        }
        //获取对应的字段属性
        List<PropertyValue> propertyValues=parsePropertyValues(beanElement);
        //这里先简单化，就不去实现是否需要产生代理对象了
        BeanDefinition beanDefinition = new BeanDefinition(id, Class.forName(className), propertyValues);
        beanFactory.registerBean(beanDefinition);
        return beanDefinition;
    }

    private List<PropertyValue> parsePropertyValues(Element beanElement) {
        List<PropertyValue> propertyValues = new ArrayList<PropertyValue>();
        //获取该beanElement下的property标签
        List<Element> propertyElements = beanElement.selectNodes("/property");
        for (Element propertyElement : propertyElements) {
            //获取该字段名称
            String name = propertyElement.attributeValue("name");
            //有么只有value,要么只有ref,先读取是否有value属性,而value都是对应基本类型的
            String value = propertyElement.attributeValue("value");
            if (value == null) {
                //value为空，代表用的是ref属性
                String ref = propertyElement.attributeValue("ref");
                propertyValues.add(new PropertyValue(name, null, ref));
            }else{
                propertyValues.add(new PropertyValue(name, value, null));
            }
        }
        return propertyValues;
    }

    public DefaultListableBeanFactory getBeanFactory() {
        if (beanFactory == null) {
            beanFactory=new DefaultListableBeanFactory();
        }
        return beanFactory;
    }

    public List<InterceptorDefinition> getInterceptors() {
        return interceptors;
    }
}
